home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Modules
/
BackSpaceModules
/
Source
/
Sperm
/
SpermView.m
< prev
next >
Wrap
Text File
|
1993-06-17
|
9KB
|
444 lines
/*
The animation guts from a freely distributable X program:
xsperm.c
Drew Olbrich, Febrary 1991
Note -- This code originally served as a demonstration
of how to do animation under X. The "guts" of the program
which draws the sperm are consequently located in one huge
chunk in the update_display() routine, and can be easily
cut out.
The animation function wrapped in a NeXTstep View subclass by Ali Ozer, May 91
Very minor changes so this thing works as a screen saver module by sam streeper,
August 91
The "oneStep" method computes new locations.
*/
#import "SpermView.h"
#import "Thinker.h"
#import <appkit/appkit.h>
#define VEC_DOT(x, y) (x[0]*y[0] + x[1]*y[1])
#define VEC_LEN(x) (sqrt(x[0]*x[0] + x[1]*x[1]))
#define VEC_SET(x, a, b) x[0] = a, x[1] = b
#define VEC_COPY(y, x) y[0] = x[0], y[1] = x[1]
#define VEC_NEG(x) x[0] = -x[0], x[1] = -x[1]
#define VEC_ADD(z, x, y) z[0] = x[0] + y[0], z[1] = x[1] + y[1]
#define VEC_SUB(z, x, y) z[0] = x[0] - y[0], z[1] = x[1] - y[1]
#define VEC_MULT(x, a) x[0] *= a, x[1] *= a
#define VEC_DIV(x, a) x[0] /= a, x[1] /= a
#define VEC_ADDS(z, x, a, y) z[0] = x[0] + (a)*y[0], z[1] = x[1] + (a)*y[1]
#define VEC_NORM(x) { double l = VEC_LEN(x); VEC_DIV(x, l); }
#define MINRAD 0.1
#define RADSTEP 2.0
#define MAXRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP * RADSTEP)
#define INITRAD (MINRAD * RADSTEP * RADSTEP * RADSTEP)
// RANDINT(n) returns an integer 0..n-1
// RANDFLOAT(f) returns a float [0..f] (inclusive on both ends)
#define RANDINT(n) (random() % (n))
#define RANDFLOAT(f) (((f) * (float)(random() & 0x0ffff)) / (float)0x0ffff)
@implementation SpermView
- initFrame:(const NXRect *)rect
{
[super initFrame:rect];
[self allocateGState]; // For faster lock/unlockFocus
dir = 1.0;
rad = INITRAD;
[self getSpermCount];
[self getLineWidth];
[self getUseColor];
[inspectorPanel display];
color = NX_COLORWHITE;
alreadyInitialized = NO;
randCount1 = 100;
randCount2 = 200;
uPath = newUserPath();
return self;
}
- (void)initializeLine:(int)i
{
double angle = RANDFLOAT(10.0) + 5.0;
prevX[i][0] = x[i][0] = (double) (RANDINT((int)NX_WIDTH(&bounds)));
prevX[i][1] = x[i][1] = (double) (RANDINT((int)NX_HEIGHT(&bounds)));
v[i][0] = RANDFLOAT(2.0) - 1.0;
v[i][1] = RANDFLOAT(2.0) - 1.0;
sine[i] = sin(angle*M_PI/180.0);
cosine[i] = cos(angle*M_PI/180.0);
vel[i] = RANDFLOAT(4.0) + 4.0;
VEC_NORM(v[i]);
}
- (void)getFocusFromEvent:(NXEvent *)event
{
NXPoint loc = event->location;
[self convertPoint:&loc fromView:nil];
mouse[0] = loc.x;
mouse[1] = loc.y;
}
- (BOOL)acceptsFirstMouse
{ return YES;
}
- mouseDown:(NXEvent *)event
{
[self getFocusFromEvent:event];
return self;
}
- effectOne
{
VECTOR y;
int i;
dir *= -1.0;
for (i = 0; i < MAXCOUNT; i++) {
VEC_COPY(y, v[i]);
if (dir == -1.0) {
v[i][0] = y[1];
v[i][1] = -y[0];
} else {
v[i][0] = -y[1];
v[i][1] = y[0];
}
}
return self;
}
- effectTwo
{
int i;
for (i = 0; i < MAXCOUNT; i++) {
v[i][0] = -v[i][0];
}
return self;
}
- effectThree
{
int i;
for (i = 0; i < MAXCOUNT; i++) {
v[i][1] = -v[i][1];
}
return self;
}
- effectFour
{
[self effectTwo];
[self effectThree];
return self;
}
- effectFive
{
[self effectOne];
[self effectFour];
return self;
}
- effectSix
{
rad = MIN(rad * RADSTEP, MAXRAD);
return self;
}
- effectSeven
{
rad = MAX(rad / RADSTEP, MINRAD);
return self;
}
- doEffectNumber:(int)val
{
switch (val) {
case 0: [self effectOne]; break;
case 1: [self effectTwo]; break;
case 2: [self effectThree]; break;
case 3: [self effectFour]; break;
case 4: [self effectFive]; break;
case 5: [self effectSix]; break;
case 6: [self effectSeven]; break;
default: break;
}
return self;
}
- oneStep
{
int i, cnt;
POINT lLeft, uRight;
NXRect eraseRect;
uRight[0] = lLeft[0] = x[0][0];
uRight[1] = lLeft[1] = x[0][1];
for (i = 0; i < count; i++) {
VECTOR w, y;
POINT p;
double r;
for (cnt = 0; cnt < 2; cnt++) {
if (prevX[i][cnt] < lLeft[cnt]) lLeft[cnt] = prevX[i][cnt];
else if (prevX[i][cnt] > uRight[cnt]) uRight[cnt] = prevX[i][cnt];
if (x[i][cnt] < lLeft[cnt]) lLeft[cnt] = x[i][cnt];
else if (x[i][cnt] > uRight[cnt]) uRight[cnt] = x[i][cnt];
}
prevX[i][0] = x[i][0]; /* old location */
prevX[i][1] = x[i][1];
VEC_SUB(w, x[i], mouse);
VEC_NORM(w);
VEC_COPY(y, w);
w[0] = y[0]*cosine[i] - dir*y[1]*sine[i];
w[1] = y[1]*cosine[i] + dir*y[0]*sine[i];
VEC_ADDS(p, mouse, rad*(160.0 - vel[i]*20.0), w);
VEC_SUB(w, p, x[i]);
r = VEC_LEN(w);
VEC_DIV(w, r);
VEC_ADDS(v[i], v[i], 1.0, w);
VEC_NORM(v[i]);
VEC_MULT(v[i], vel[i]);
VEC_ADD(x[i], x[i], v[i]);
}
NXSetRect (&eraseRect, lLeft[0], lLeft[1], uRight[0]-lLeft[0], uRight[1]-lLeft[1]);
NXInsetRect (&eraseRect, -1.0-lineWidth, -1.0-lineWidth);
PSsetgray(0);
NXRectFill(&eraseRect);
[self drawPath];
if (--randCount1 < 0)
{
randCount1 = RANDINT(700);
mouse[0] = randBetween(0,bounds.size.width);
mouse[1] = randBetween(0,bounds.size.height);
}
if (--randCount2 < 0)
{
randCount2 = RANDINT(600);
[self doEffectNumber:(randCount2 % 7)];
}
return self;
}
// Modify "orig" by upto plus or minus "by" keeping it in the specified range...
static float randMod(float orig, float by, float min, float max)
{
orig = orig + RANDFLOAT(by * 2.0) - by;
return (orig < min) ? min : ((orig > max) ? max : orig);
}
- drawPath
{
int cnt;
PSsetlinewidth (lineWidth);
if (useColors) {
color = NXConvertRGBToColor(randMod(NXRedComponent(color), 0.05, 0.0, 1.0),
randMod(NXGreenComponent(color), 0.05, 0.0, 1.0),
randMod(NXBlueComponent(color), 0.05, 0.0, 1.0));
}
else color = NX_COLORWHITE;
NXSetColor (color);
beginUserPath(uPath, NO);
for (cnt = 0; cnt < count; cnt++) {
UPmoveto(uPath, (float)prevX[cnt][0], (float)prevX[cnt][1]);
UPlineto(uPath, (float)x[cnt][0], (float)x[cnt][1]);
}
closePath(uPath);
endUserPath(uPath, dps_ustroke);
sendUserPath(uPath);
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
if (!rects || !rectCount) return self;
PSsetgray(NX_BLACK);
NXRectFill(rects);
[self drawPath];
return self;
}
- newWindow
{
mouse[0] = randBetween(0,bounds.size.width);
mouse[1] = randBetween(0,bounds.size.height);
return self;
}
- free
{
freeUserPath(uPath);
return [super free];
}
- setNumLines:sender
{
int i;
int oldCount = count;
char str[100];
// set the number of lines
count = MIN(MAXCOUNT, MAX([sender intValue], 1));
// initialize velocities & such
for (i = oldCount; i < count; i++) {
[self initializeLine:i];
}
[self display];
sprintf(str,"%d", count);
NXWriteDefault([NXApp appName], "SpermViewCount", str);
return self;
}
- getSpermCount
{
const char *ptr;
int val;
[spermCountSlider setMinValue: 10];
[spermCountSlider setMaxValue: MAXCOUNT];
ptr = NXGetDefaultValue([NXApp appName], "SpermViewCount");
if (ptr)
{
sscanf(ptr,"%d",&val);
if (val >= 10 && val <= MAXCOUNT) count = val;
else count = MAXCOUNT;
}
else count = MAXCOUNT;
return self;
}
- setUseColor:sender
{
useColors = [sender state];
if (useColors)
NXWriteDefault([NXApp appName], "SpermViewColor", "Yes");
else
NXRemoveDefault([NXApp appName], "SpermViewColor");
return self;
}
- getUseColor
{
const char *ptr;
ptr = NXGetDefaultValue([NXApp appName], "SpermViewColor");
if (!ptr || !strcmp(ptr,"No")) useColors = NO;
else useColors = YES;
return self;
}
- setLineWidth:sender
{
char str[50];
lineWidth = MAX([sender floatValue], 0.0);
sprintf(str,"%5.1f", lineWidth);
NXWriteDefault([NXApp appName], "SpermViewWidth", str);
return self;
}
- getLineWidth
{
const char *ptr;
float val;
[spermWidthSlider setMinValue: 0];
[spermWidthSlider setMaxValue: 8];
ptr = NXGetDefaultValue([NXApp appName], "SpermViewWidth");
if (ptr)
{
sscanf(ptr,"%f",&val);
if (val >= 0 && val <= 8) lineWidth = val;
else lineWidth = 0;
}
else lineWidth = 0;
return self;
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
if (!alreadyInitialized)
{ int i;
mouse[0] = NX_MIDX(&bounds);
mouse[1] = NX_MIDY(&bounds);
for (i = 0; i < MAXCOUNT; i++) {
[self initializeLine:i];
}
alreadyInitialized = YES;
}
[self newWindow];
return self;
}
- (const char *)windowTitle
{ return "Sperm";
}
- (BOOL) useBufferedWindow;
{ return YES;
}
- inspector:sender
{
char buf[MAXPATHLEN];
if (!inspectorPanel)
{
[NXBundle getPath:buf forResource:"sperm" ofType:"nib" inDirectory:[sender moduleDirectory:"Sperm"] withVersion:0];
[NXApp loadNibFile:buf owner:self withNames:NO];
[spermCountSlider setIntValue:count];
[spermWidthSlider setFloatValue:lineWidth];
[colorButton setState: (useColors ? 1:0)];
}
return inspectorPanel;
}
@end